home *** CD-ROM | disk | FTP | other *** search
/ Internet Info 1994 March / Internet Info CD-ROM (Walnut Creek) (March 1994).iso / networking / news / readers / nn / nn6.4.patch9 < prev    next >
Encoding:
Text File  |  1990-07-31  |  53.9 KB  |  2,056 lines

  1.          This is an official patch to nn release 6.4
  2.          -------------------------------------------
  3.  
  4.                    PATCH #9
  5.  
  6.                 Priority: MEDIUM
  7.  
  8.  
  9. These patches fix a trivial, but irritating bug introduced in patch
  10. #8, as well as a few other bugs which have been on the "to-be-fixed"
  11. list for some time.  Here are some of the fixes:
  12.  
  13. - Setting "record" in the init file would cause nn to dump core.
  14.  
  15. - By mistake I had used one of those "wonderful" tri-graphs defined by
  16.   ANSI-C in a string constant ("??/" was taken as "\" which obviously
  17.   isn't a complete string - sigh!)
  18.  
  19. - Using K in reading mode to auto-select on a subject no longer skips
  20.   to the next group immediately.  Also it no longer leaves the current
  21.   article selected on the next invocation unless marked so explicitly.
  22.  
  23. - If some articles are read and other are left unread in a group,
  24.   going back to the group with "P" would only show the remaining
  25.   unread articles on the menu - now the read articles which were
  26.   initially unread are included on the menu (marked read).
  27.   [there is a new previous-also-read variable to control this].
  28.  
  29. - There was a crude bug in the parsing of folders which could fool nn
  30.   if the last article in a folder was a patch (diff -c).
  31.  
  32. - There has been some problems with "nnmaster -k" not killing the
  33.   running master.  It now tries a little harder, but it still has to
  34.   be seen whether that is hard enough :-)
  35.  
  36.  
  37. New features:
  38.   
  39. - There are now so many variables that ":set all" has become a tedious
  40.   way to check a specific variable's value.  There is now a new form:
  41.       :set /regexp
  42.   which will list only the variables matching the given regular expression.
  43.  
  44. - When completing a variable name in the :set command, the current
  45.   value of the variable is shown in the message line.
  46.  
  47. - The built-in pager can now scroll article text if the variable
  48.   scroll-last-lines is set to -1.  It may also be set to a positive
  49.   number in which case, nn will only scroll if there are less than
  50.   that many lines left in the article.  Try setting it to 10 to see
  51.   its intended use (to scroll only the last page).
  52.  
  53. - There is a new "admins" file which can be created in the CLIENT
  54.   directory.  It should contain the login names of the users which are
  55.   allowed to use the more advanced features of nnacct, e.g. -ar.
  56.  
  57. - Variables can now be set on the command line using arguments of the
  58.   form variable=value.
  59.  
  60.  
  61. As usual, all changes are described in the updated RELEASE_NOTES file
  62. (read that for more details about this patch).  Thanks to all who
  63. reported bugs and provided fixes.
  64.  
  65. To apply this patch, use nn's :patch command, or run this command from
  66. the shell in the root of the nn source tree:
  67.     patch -p0 < this-article
  68.  
  69. Then run "make all" and "./inst u".
  70.  
  71. ++Kim Storm
  72.  
  73. *** ./LAST/account.c    Mon Jul 16 17:38:38 1990
  74. --- account.c    Wed Jul 18 10:20:57 1990
  75. ***************
  76. *** 349,354 ****
  77. --- 349,378 ----
  78.       fprintf(stderr, "ZERO of accounts failed -- check permissions etc.\n");
  79.   }
  80.   
  81. + static news_admin(caller)
  82. + char *caller;
  83. + {
  84. +     FILE *adm;
  85. +     char line[80];
  86. +     int len, ok;
  87. +     adm = open_file(relative(lib_directory, "admins"), OPEN_READ);
  88. +     if (adm == NULL) return 2;
  89. +     len = strlen(caller);
  90. +     ok = 0;
  91. +     while (fgets(line, 80, adm)) {
  92. +     if (line[len] != NL) continue;
  93. +     line[len] = NUL;
  94. +     if (strcmp(caller, line) == 0) {
  95. +         ok = 1;
  96. +         break;
  97. +     }
  98. +     }
  99. +     fclose(adm);
  100. +     return ok;
  101. + }
  102.   main(argc, argv)
  103.   int argc;
  104.   char *argv[];
  105. ***************
  106. *** 371,376 ****
  107. --- 395,403 ----
  108.       }
  109.   
  110.       if (user_id != 0) {
  111. +     caller = user_name();
  112. +     if (news_admin(caller) == 1) goto caller_ok;
  113.       if (report_all) {
  114.           fprintf(stderr, "Only root can request complete reports\n");
  115.           exit(9);
  116. ***************
  117. *** 391,403 ****
  118.           fprintf(stderr, "Only root can request reports for other users\n");
  119.           exit(9);
  120.       }
  121. -     if ((caller = getlogin()) == NULL)
  122. -         if ((caller = user_name()) == NULL)
  123. -         caller = "UNKNOWN";
  124.       } else
  125.       caller = "root";
  126.   
  127.       if ((new_policy >= 0 || new_quota >= 0) && users == 0) {
  128.       fprintf(stderr, "usage: %s -pPOLICY -qQUOTA user...\n", argv[0]);
  129.       exit(1);
  130. --- 418,427 ----
  131.           fprintf(stderr, "Only root can request reports for other users\n");
  132.           exit(9);
  133.       }
  134.       } else
  135.       caller = "root";
  136.   
  137. +  caller_ok:
  138.       if ((new_policy >= 0 || new_quota >= 0) && users == 0) {
  139.       fprintf(stderr, "usage: %s -pPOLICY -qQUOTA user...\n", argv[0]);
  140.       exit(1);
  141. *** ./LAST/answer.c    Mon Jul 16 17:38:39 1990
  142. --- answer.c    Tue Jul 17 20:26:23 1990
  143. ***************
  144. *** 249,255 ****
  145.       clrdisp();
  146.       pg_init(0, 1);
  147.       }
  148. !     pg_regexp = regcomp(expr);
  149.   
  150.       while (fgets(line, 512, f)) {
  151.       if (pg_regexp && regexec_fold(pg_regexp, line) == 0) continue;
  152. --- 249,255 ----
  153.       clrdisp();
  154.       pg_init(0, 1);
  155.       }
  156. !     if (expr) pg_regexp = regcomp(expr);
  157.   
  158.       while (fgets(line, 512, f)) {
  159.       if (pg_regexp && regexec_fold(pg_regexp, line) == 0) continue;
  160. ***************
  161. *** 846,852 ****
  162.   
  163.       prompt(who_am_i == I_AM_POST ? "Newsgroups: " : "\1POST to group\1 ");
  164.   
  165. !     strcpy(brk_chars, "??/");
  166.       brk_chars[0] = help_key;
  167.       str = get_s(current_group ? current_group->group_name : NONE,
  168.               group_name, brk_chars, group_completion);
  169. --- 846,852 ----
  170.   
  171.       prompt(who_am_i == I_AM_POST ? "Newsgroups: " : "\1POST to group\1 ");
  172.   
  173. !     strcpy(brk_chars, " /?");
  174.       brk_chars[0] = help_key;
  175.       str = get_s(current_group ? current_group->group_name : NONE,
  176.               group_name, brk_chars, group_completion);
  177. *** ./LAST/articles.c    Tue Jun 12 11:46:26 1990
  178. --- articles.c    Tue Jul 17 16:03:55 1990
  179. ***************
  180. *** 285,291 ****
  181.       leave_attr = A_LEAVE_NEXT;
  182.   
  183.       if (!(flags & ACC_SPEW_MODE))
  184. !     use_newsrc(gh, (flags & ACC_ORIG_NEWSRC) ? 1 : 0);
  185.   
  186.       if ((flags & ACC_EXTRA_ARTICLES) == 0)
  187.       mark_memory(&mem_marker);
  188. --- 285,292 ----
  189.       leave_attr = A_LEAVE_NEXT;
  190.   
  191.       if (!(flags & ACC_SPEW_MODE))
  192. !     use_newsrc(gh, (flags & ACC_MERGED_NEWSRC) ? 2 :
  193. !                (flags & ACC_ORIG_NEWSRC) ? 1 : 0);
  194.   
  195.       if ((flags & ACC_EXTRA_ARTICLES) == 0)
  196.       mark_memory(&mem_marker);
  197. ***************
  198. *** 371,376 ****
  199. --- 372,380 ----
  200.   
  201.       ah->attr = test_article(ah);
  202.   
  203. +     if (flags & ACC_MERGED_NEWSRC) {
  204. +         if (ah->attr == A_KILL) continue;
  205. +     } else
  206.       if (ah->attr != A_READ && (flags & ACC_ONLY_READ_ARTICLES))
  207.           continue;
  208.   
  209. ***************
  210. *** 412,417 ****
  211. --- 416,423 ----
  212.           break;
  213.   
  214.        case A_READ:
  215. +         if (flags & ACC_MERGED_NEWSRC) break;
  216.           if (!(flags & (ACC_ALSO_READ_ARTICLES | ACC_ONLY_READ_ARTICLES)))
  217.           if (ah->a_number > gh->last_article)
  218.               continue;
  219. *** ./LAST/articles.h    Tue Jun 12 11:46:26 1990
  220. --- articles.h    Tue Jul 17 15:09:37 1990
  221. ***************
  222. *** 53,55 ****
  223. --- 53,56 ----
  224.   #define ACC_ON_SUBJECT        FLAG(13) /* match on subject (also) */
  225.   #define ACC_DO_KILL        FLAG(14) /* do auto-kill/select */
  226.   #define    ACC_PARSE_VARIABLES    FLAG(15) /* kill, split, etc. */
  227. + #define ACC_MERGED_NEWSRC    FLAG(16) /* merge orig and cur .newsrc */
  228. *** ./LAST/digest.c    Thu Apr 26 16:35:07 1990
  229. --- digest.c    Wed Jul 18 13:17:01 1990
  230. ***************
  231. *** 165,170 ****
  232. --- 165,172 ----
  233.           if (line_type[backup_index] & (LN_ASTERISK | LN_END_OF)) break;
  234.           }
  235.   
  236. +     digest.dg_lpos = backup_p[backup_index];
  237.       if (digest.dg_lines == 0) return 0;
  238.   
  239.       while (--backup_count >= 0) {
  240. *** ./LAST/doc/RELEASE_NOTES    Mon Jul 16 17:38:41 1990
  241. --- doc/RELEASE_NOTES    Wed Jul 18 18:57:12 1990
  242. ***************
  243. *** 967,973 ****
  244. --- 967,1055 ----
  245.   From:    Tony Wilson <wilson@issun3.stc.nl>
  246.   Fixed:    Patch #8 [menu.c  -- buffer overrun in get_purpose]
  247.   
  248. + Prog:    nn
  249. + Title:    Cannot set "record" variable (dumps core).  (bug in patch 8)
  250. + From:    beldar@mips.com (Gardner Cohen) + fix
  251. +     Damian Chu <dac@doc.ic.ac.uk> + fix
  252. +     Tony Wilson <wilson%shapetc@nluug.nl>
  253. +     rock@warp.Eng.Sun.COM (Bill Petro)
  254. + Fixed:    Patch #9 [variable.c]
  255. + Prog:    nn
  256. + Title:    Crash if all entries in kill file are obsolete.
  257. + From:    olson%anchor.esd@sgi.com (Dave Olson)
  258. + Fixed:    Patch #9 [kill.c]
  259. + Prog:    nn
  260. + Title:    Patch #8 uses a tri-graph in a string constant.
  261. + From:    Bo Kullmar <bk@kullmar.se>
  262. + Fixed:    Patch #9 [answer.c]
  263. + Prog:    nn
  264. + Title:    "P" to a group with unread articles should include read articles.
  265. + From:    Pekka Kyt|laakso <netmgr@csc.fi>
  266. +     isaac@goanna.cs.rmit.oz.au (Isaac Balbin)
  267. + Fixed:    Patch #9 [articles.c articles.h group.c newsrc.c nn.c variable.c nn.1]
  268. +     There is a new variable "previous-also-read" which can be
  269. +     unset to get the old "buggy" behaviour.
  270. + Prog:    nn
  271. + Title:    Only some commands could be bound to user-defined keymaps.
  272. + From:    KFS
  273. + Fixed:    Patch #9 [keymap.c]
  274. + Prog:    nn
  275. + Title:    If a command cannot be bound in a specific map, the error
  276. +     message incorrectly says that the command does not exist.
  277. + From:    rock@warp.Eng.Sun.COM (Bill Petro)
  278. + Fixed:    Patch #9 [init.c keymap.c macro.c]
  279. + Prog:    nnpost, nn
  280. + Title:    Entering "?" to Newsgroups: prompt will give an (ignored) REGEXP ERROR
  281. + From:    dean@coplex.uucp (Dean Brooks)
  282. + Fixed:    Patch #9 [answer.c]
  283. + Prog:    nn
  284. + Title:    Some "export" declarations were missing the type name ("int").
  285. + From:    eps@toaster.SFSU.EDU (Eric P. Scott) + fix
  286. + Fixed:    Patch #9 [expire.c news.c nntp.c]
  287. + Prog:    nnmaster
  288. + Title:    "nnmaster -k" sometimes fails to kill running master.
  289. + From:    ...
  290. + Fixed:    Patch #9 [master.c]
  291. +     It seems like the HUP signal is lost somewhere; the fix involves
  292. +     also sending a TERM signal and introducing some delays.
  293. + Prog:    nnmaster
  294. + Title:    If nnmaster is started in the nn source directory (by root), all
  295. +     sorts of problems with files changing owner to root etc. could happen.
  296. + From:    Paul Graham <pjg@acsu.buffalo.edu>
  297. + Fixed:    Patch #9 [master.c]
  298. + Prog:    nn, nnacct
  299. + Title:    The login name of the user is not always found correctly (it's blank).
  300. + From:    asd@mace.cc.purdue.edu (Kareth)
  301. + Fixed:    Patch #9 [global.c]
  302. +     It seems that if nn is invoked from xterm, getlogin may return an
  303. +     empty string instead of the user name.  nn now checks for this
  304. +     return value and uses other ways to get the user name (passwd, $USER).
  305. + Prog:    nn
  306. + Title:    If last article in a folder is a patch, that article's body by be lost.
  307. + From:    asd@mace.cc.purdue.edu (Kareth)
  308. + Fixed:    Patch #9 [digest.c]
  309. + Prog:    nn
  310. + Title:    K command in reading mode would skip to next article also when making
  311. +     an auto-select entry.  Furthermore it would keep the article selected.
  312. + From:    chuq@Apple.COM (The Bounty Hunter)
  313. + Fixed:    Patch #9 [menu.c more.c]
  314.   
  315.   New features since initial 6.4.0 release
  316.   ----------------------------------------
  317.   
  318. ***************
  319. *** 1252,1254 ****
  320. --- 1334,1367 ----
  321.   Title:    The proper "re prefix" is now also shown on the "*" header in read mode
  322.   From:    Uwe Doering (adapted by KFS)
  323.   Added:    Patch #8 [menu.c more.c variable.c]
  324. + Prog:    nn
  325. + Title:    Added ":set /regexp" to list only selected variables.
  326. + From:    KFS
  327. + Added:    Patch #9 [init.c variable.c nn.1]
  328. + Prog:    nn
  329. + Title:    Completion of a variable name (e.g. :set au<space>) will now
  330. +     show the current value of the variable in the message line.
  331. + From:    KFS
  332. + Added:    Patch #9 [init.c variable.c term.c nn.1]
  333. + Prog:    nn
  334. + Title:    New "scroll-last-lines" variable to make nn scroll last page only.
  335. + From:    KFS on request from news@m2xenix.psg.com (Randy Bush)
  336. + Added:    Patch #9 [more.c variable.c nn.1]
  337. +     If < 0, nn will scroll text rather than display each new page from
  338. +     the top of the screen.    If > 0, then nn will scroll the last
  339. +     "s-l-l" lines of the article (and display the rest as usual).
  340. + Prog:    nnacct
  341. + Title:    New "$CLIENT/admins" file where the login names of the privileged
  342. +     news administrators allowed to run nnacct besides root can be listed.
  343. + From:    KFS on request from asd@mace.cc.purdue.edu (Kareth)
  344. + Added:    Patch #9 [account.c nnacct.1m]
  345. + Prog:    nn
  346. + Title:    Variables can now be set on nn command line with var=value.
  347. + From:    KFS on request from asd@mace.cc.purdue.edu (Kareth)
  348. + Added:    Patch #9 [sequence.c]
  349. *** ./LAST/expire.c    Mon Jun 25 15:46:39 1990
  350. --- expire.c    Tue Jul 17 20:26:16 1990
  351. ***************
  352. *** 21,29 ****
  353.    *    3: recollect group to expire (also if "min" still exists)
  354.    */
  355.   
  356. ! export expire_method = 1;    /* expire method */
  357. ! export recollect_method = 1;    /* recollection method -- see do_expire */
  358. ! export expire_level = 0;    /* automatic expiration detection */
  359.   
  360.   #ifdef HAVE_DIRECTORY
  361.   
  362. --- 21,29 ----
  363.    *    3: recollect group to expire (also if "min" still exists)
  364.    */
  365.   
  366. ! export int expire_method = 1;    /* expire method */
  367. ! export int recollect_method = 1; /* recollection method -- see do_expire */
  368. ! export int expire_level = 0;    /* automatic expiration detection */
  369.   
  370.   #ifdef HAVE_DIRECTORY
  371.   
  372. *** ./LAST/global.c    Mon Jul 16 17:38:42 1990
  373. --- global.c    Wed Jul 18 16:29:23 1990
  374. ***************
  375. *** 93,101 ****
  376.   
  377.   sig_type catch_hangup(n)
  378.   {
  379.       signal(n, SIG_IGN);
  380.   
  381. -     s_hangup++;
  382.   #ifdef FAKE_INTERRUPT
  383.       if (fake_keyb_siglist[n] && arm_fake_keyb_sig)
  384.           longjmp(fake_keyb_sig, 1);
  385. --- 93,101 ----
  386.   
  387.   sig_type catch_hangup(n)
  388.   {
  389. +     s_hangup = 1;
  390.       signal(n, SIG_IGN);
  391.   
  392.   #ifdef FAKE_INTERRUPT
  393.       if (fake_keyb_siglist[n] && arm_fake_keyb_sig)
  394.           longjmp(fake_keyb_sig, 1);
  395. ***************
  396. *** 188,193 ****
  397. --- 188,199 ----
  398.   
  399.       process_id = getpid();
  400.   
  401. + #ifdef CLIENT_DIRECTORY
  402. +     lib_directory = CLIENT_DIRECTORY;
  403. + #else
  404. +     lib_directory = LIB_DIRECTORY;
  405. + #endif
  406.   #ifdef NEWS_DIRECTORY
  407.       news_directory = NEWS_DIRECTORY;
  408.   #else
  409. ***************
  410. *** 228,239 ****
  411.       /* this may later be changed by nntp_check */
  412.       news_active = mk_file_name(news_lib_directory, "active");
  413.   
  414. - #ifdef CLIENT_DIRECTORY
  415. -     lib_directory = CLIENT_DIRECTORY;
  416. - #else
  417. -     lib_directory = LIB_DIRECTORY;
  418. - #endif
  419.   #ifdef MASTER_DIRECTORY
  420.       master_directory = MASTER_DIRECTORY;
  421.   #else
  422. --- 234,239 ----
  423. ***************
  424. *** 606,623 ****
  425.   {
  426.       static char *user = NULL;
  427.       struct passwd *pw, *getpwuid();
  428.   
  429.       if (who_am_i == I_AM_MASTER) return "M";
  430.       if (who_am_i == I_AM_EXPIRE) return "X";
  431.   
  432.       if (user == NULL) {
  433. !     extern char *getlogin();
  434. !     if (user = getlogin()) return user;
  435.       pw = getpwuid((int)user_id);
  436. !     if (pw == NULL) user = "?";
  437. !     user = copy_str(pw->pw_name);
  438.       }
  439.   
  440.       return user;
  441.   }
  442.   
  443. --- 606,634 ----
  444.   {
  445.       static char *user = NULL;
  446.       struct passwd *pw, *getpwuid();
  447. +     extern char *getlogin(), *getenv();
  448.   
  449.       if (who_am_i == I_AM_MASTER) return "M";
  450.       if (who_am_i == I_AM_EXPIRE) return "X";
  451.   
  452.       if (user == NULL) {
  453. !     user = getlogin();
  454. !     if (user != NULL && *user != NUL) goto out;
  455.       pw = getpwuid((int)user_id);
  456. !     if (pw != NULL && pw->pw_name[0] != NUL) {
  457. !         user = copy_str(pw->pw_name);
  458. !         goto out;
  459. !     }
  460. !     user = getenv("LOGNAME");
  461. !     if (user != NULL && *user != NUL) goto out;
  462. !     user = getenv("USER");
  463. !     if (user != NULL && *user != NUL) goto out;
  464. !     user = "?";
  465.       }
  466.   
  467. +  out:
  468.       return user;
  469.   }
  470.   
  471. *** ./LAST/group.c    Mon Jul 16 17:38:44 1990
  472. --- group.c    Tue Jul 17 17:21:41 1990
  473. ***************
  474. *** 248,254 ****
  475.   
  476.        update_unsafe:
  477.       if ((gh->current_first = first_art) < 0) {
  478. !         if (access_mode & ACC_ORIG_NEWSRC)
  479.           gh->current_first = gh->first_article + 1;
  480.           else if (access_mode & ACC_ALSO_READ_ARTICLES)
  481.           gh->current_first = gh->first_db_article;
  482. --- 248,254 ----
  483.   
  484.        update_unsafe:
  485.       if ((gh->current_first = first_art) < 0) {
  486. !         if (access_mode & (ACC_ORIG_NEWSRC | ACC_MERGED_NEWSRC))
  487.           gh->current_first = gh->first_article + 1;
  488.           else if (access_mode & ACC_ALSO_READ_ARTICLES)
  489.           gh->current_first = gh->first_db_article;
  490. *** ./LAST/init.c    Mon Jul 16 17:38:44 1990
  491. --- init.c    Tue Jul 17 20:07:46 1990
  492. ***************
  493. *** 357,362 ****
  494. --- 357,363 ----
  495.           for (p = head; *p; )
  496.               if (*p++ == SP) return -1;
  497.           other_compl = var_completion;
  498. +         var_compl_opts(tail - buf);
  499.           tail = NULL;
  500.           temp = var_completion(head, len);
  501.           break;
  502. ***************
  503. *** 687,692 ****
  504. --- 688,697 ----
  505.       code = lookup_command(argv(3), map_def->km_flag & (K_ONLY_MENU|K_ONLY_MORE));
  506.       
  507.       switch (code) {
  508. +      case K_INVALID-1:
  509. +     init_message("Cannot bind '%s' in '%s' map", argv(3), argv(1));
  510. +     goto out;
  511.        case K_EQUAL_KEY:
  512.       if (argv(4) == NULL) goto mac_err;
  513.       code = map[parse_key(argv(4))];
  514. ***************
  515. *** 882,895 ****
  516.       }
  517.   
  518.       CASE( "set" ) {
  519. !         if (ARGTAIL == NULL) {
  520. !         disp_variables(0);
  521.           return AC_REDRAW;
  522.           }
  523.   
  524.           cmd = argv(1);    /* get ARGTAIL right */
  525.           if (cmd != NULL && strcmp(cmd, "all") == 0) {
  526. !         disp_variables(1);
  527.           return AC_REDRAW;
  528.           }
  529.   
  530. --- 887,900 ----
  531.       }
  532.   
  533.       CASE( "set" ) {
  534. !         if (ARGTAIL == NULL || ARGTAIL[0] == '/') {
  535. !         disp_variables(0, ARGTAIL);
  536.           return AC_REDRAW;
  537.           }
  538.   
  539.           cmd = argv(1);    /* get ARGTAIL right */
  540.           if (cmd != NULL && strcmp(cmd, "all") == 0) {
  541. !         disp_variables(1, (char *)NULL);
  542.           return AC_REDRAW;
  543.           }
  544.   
  545. ***************
  546. *** 998,1004 ****
  547.   
  548.           alt_cmd_key = lookup_command(sw_string,
  549.                    in_menu_mode ? K_ONLY_MENU : K_ONLY_MORE);
  550. !         if (alt_cmd_key != K_INVALID && alt_cmd_key != K_HELP) {
  551.           if (alt_cmd_key == K_MACRO) {
  552.               if (ARGTAIL == NULL) break;
  553.               alt_cmd_key |= atoi(ARGTAIL);
  554. --- 1003,1009 ----
  555.   
  556.           alt_cmd_key = lookup_command(sw_string,
  557.                    in_menu_mode ? K_ONLY_MENU : K_ONLY_MORE);
  558. !         if (alt_cmd_key > K_INVALID && alt_cmd_key != K_HELP) {
  559.           if (alt_cmd_key == K_MACRO) {
  560.               if (ARGTAIL == NULL) break;
  561.               alt_cmd_key |= atoi(ARGTAIL);
  562. *** ./LAST/keymap.c    Mon Jul 16 17:38:45 1990
  563. --- keymap.c    Tue Jul 17 20:07:47 1990
  564. ***************
  565. *** 583,589 ****
  566.           if (cnmp->cmd_restriction == restriction) break;
  567.            case 0:
  568.           if (cnmp->cmd_restriction == 0) break;
  569. !         return K_INVALID;
  570.            default:
  571.           break;
  572.           }
  573. --- 583,589 ----
  574.           if (cnmp->cmd_restriction == restriction) break;
  575.            case 0:
  576.           if (cnmp->cmd_restriction == 0) break;
  577. !         return K_INVALID-1;
  578.            default:
  579.           break;
  580.           }
  581. ***************
  582. *** 904,910 ****
  583.       m = &keymaps[ix];
  584.       m->km_name = copy_str(name);
  585.       m->km_map = newobj(int, KEY_MAP_SIZE);
  586. !     m->km_flag = 0;
  587.       for (kp = m->km_map; kp < &(m->km_map)[KEY_MAP_SIZE]; ) *kp++ = K_UNBOUND;
  588.   
  589.       return ix;
  590. --- 904,910 ----
  591.       m = &keymaps[ix];
  592.       m->km_name = copy_str(name);
  593.       m->km_map = newobj(int, KEY_MAP_SIZE);
  594. !     m->km_flag = K_ONLY_MORE | K_ONLY_MENU;
  595.       for (kp = m->km_map; kp < &(m->km_map)[KEY_MAP_SIZE]; ) *kp++ = K_UNBOUND;
  596.   
  597.       return ix;
  598. *** ./LAST/kill.c    Tue Jun 12 11:46:39 1990
  599. --- kill.c    Tue Jul 17 11:31:17 1990
  600. ***************
  601. *** 793,806 ****
  602.       /* DB check: if database is rebuilt, group numbers may change */
  603.       if (header.ckh_db_check != master.db_created) goto err;
  604.   
  605. !     kill_patterns = newstr(header.ckh_pattern_size);
  606.       kill_tab = newobj(kill_list_entry, header.ckh_entries);
  607.       if (regexp_table_size = header.ckh_regexp_size)
  608.       group_regexp_table = newobj(kill_group_regexp, header.ckh_regexp_size);
  609. -     fseek(killf, (off_t)(header.ckh_entries * sizeof(entry)), 1);
  610. -     if (fread(kill_patterns, sizeof(char), (int)header.ckh_pattern_size, killf)
  611. -     !=  header.ckh_pattern_size) goto err;
  612.   
  613.       tb = group_regexp_table;
  614.   
  615. --- 793,815 ----
  616.       /* DB check: if database is rebuilt, group numbers may change */
  617.       if (header.ckh_db_check != master.db_created) goto err;
  618.   
  619. !     if (header.ckh_entries == 0) {
  620. !     fclose(killf);
  621. !     kill_file_loaded = 1;
  622. !     return 0;
  623. !     }
  624. !     if (header.ckh_pattern_size > 0) {
  625. !     kill_patterns = newstr(header.ckh_pattern_size);
  626. !     fseek(killf, (off_t)(header.ckh_entries * sizeof(entry)), 1);
  627. !     if (fread(kill_patterns, sizeof(char), (int)header.ckh_pattern_size, killf)
  628. !         !=  header.ckh_pattern_size) goto err;
  629. !     } else
  630. !     kill_patterns = newstr(1);
  631. !     
  632.       kill_tab = newobj(kill_list_entry, header.ckh_entries);
  633.       if (regexp_table_size = header.ckh_regexp_size)
  634.       group_regexp_table = newobj(kill_group_regexp, header.ckh_regexp_size);
  635.   
  636.       tb = group_regexp_table;
  637.   
  638. *** ./LAST/macro.c    Mon Jul  9 17:59:56 1990
  639. --- macro.c    Tue Jul 17 20:07:46 1990
  640. ***************
  641. *** 203,209 ****
  642.       goto ok;
  643.       }
  644.   
  645. !     if ((cmd = lookup_command(w, (K_ONLY_MENU | K_ONLY_MORE))) != K_INVALID) {
  646.       m_new(M_COMMAND);
  647.       m->m_int = GETC_COMMAND | cmd;
  648.       goto ok;
  649. --- 203,209 ----
  650.       goto ok;
  651.       }
  652.   
  653. !     if ((cmd = lookup_command(w, (K_ONLY_MENU | K_ONLY_MORE))) > K_INVALID) {
  654.       m_new(M_COMMAND);
  655.       m->m_int = GETC_COMMAND | cmd;
  656.       goto ok;
  657. *** ./LAST/man/nn.1.C    Mon Jul 16 17:38:48 1990
  658. --- man/nn.1.C    Thu Jul 19 17:52:37 1990
  659. ***************
  660. *** 42,47 ****
  661. --- 42,49 ----
  662.   .br
  663.   \- Through command line options when \fInn\fP is invoked.
  664.   .br
  665. + \- Through \fIassignments\fP on the command line when \fInn\fP is invoked.
  666. + .br
  667.   \- Through global \fBset\fP commands in the init file.
  668.   .br
  669.   \- Through \fBset\fP or \fBlocal\fP commands executed from entry macros.
  670. ***************
  671. *** 137,143 ****
  672.   .LP
  673.   The current variable settings can be shown with the
  674.   .B :set
  675. ! command without arguments.
  676.   .LP
  677.   Variables are global by default, but a local instantiation of the
  678.   variable can be created using the \fB:local\fP command.  The local
  679. --- 139,162 ----
  680.   .LP
  681.   The current variable settings can be shown with the
  682.   .B :set
  683. ! command:
  684. ! .TP
  685. ! \fB:set\fP (without arguments)
  686. ! This will give a listing of the variables which have been set in
  687. ! either the init file or interactively.
  688. ! .TP
  689. ! \fB:set all\fP
  690. ! This will give a listing of all variables.  Modified variables will be
  691. ! marked with a `*' and \fIlocal\fP variables will be marked with a `>'.
  692. ! .TP
  693. ! \fB:set /\fP\fIregexp\fP
  694. ! This will give a listing of all variables whose name matches the given
  695. ! regular expression.
  696. ! .TP
  697. ! \fB:set\fP \fIpartial-name\fP \fBspace\fP
  698. ! The \fBspace\fP (\fBcomp1-key\fP) key will complete the variable name
  699. ! as usual, but as a side effect it will display the variable's current
  700. ! value in the message line.
  701.   .LP
  702.   Variables are global by default, but a local instantiation of the
  703.   variable can be created using the \fB:local\fP command.  The local
  704. ***************
  705. *** 799,804 ****
  706. --- 818,828 ----
  707.   \fBpreview-mark-read\fP        (boolean, default true)
  708.   When set, previewing an article will mark the article as read.
  709.   .TP
  710. + \fBprevious-also-read\fP    (boolean, default true)
  711. + When set, going back to the previously read group with \fBP\fP
  712. + {\fBprevious\fP} will include articles read in the current invocation
  713. + of \fInn\fP even if there are still unread articles in the group.
  714. + .TP
  715.   \fBprint-header-lines\fP \fIfields\fP    (string, default "FDGS")
  716.   Specifies the list of header fields that are output when
  717.   an article is printed via the \fB:print\fP command and
  718. ***************
  719. *** 852,857 ****
  720. --- 876,890 ----
  721.       4:  If any references use layout 0, else layout 1.
  722.   .fi
  723.   .TP
  724. + \fBre-layout-read\fP \fIN\fP    (integer, default -1)
  725. + When the \fBheader-lines\fP variable is not set, or contains the "*"
  726. + field specifier, a line similar to the menu line will be used as the
  727. + header of the article in reading mode, including the sender's name and
  728. + the article's subject.  When this variable is negative, the subject
  729. + on this header line will be prefixed according to the \fBre-layout\fP
  730. + variable.  Otherwise, it will define the format of the "Re:" prefix to
  731. + be used instead of the \fBre-layout\fP used on the menu.
  732. + .TP
  733.   \fBread-return-next-page\fP    (boolean, default false)
  734.   When set, the \fBZ\fP {\fBread-return\fP} command will return to the
  735.   \fInext\fP menu page rather than the current menu page.
  736. ***************
  737. *** 871,876 ****
  738. --- 904,914 ----
  739.   repeat the query for a group to enter until you quit explicitly.
  740.   (Same as setting the \fB\-r\fP option permanently).
  741.   .TP
  742. + \fBreport-cost\fP        (boolean, default true)
  743. + This variable is ignored unless \fInn\fP is running with accounting
  744. + enabled (see \fInnacct\fP).  When set, \fInn\fP will report the cost
  745. + of the current session and the total on exit.
  746. + .TP
  747.   \fBresponse-check-pause\fP \fIpause\fP    (integer, default 2)
  748.   Specifies the number of seconds to wait after posting an article to
  749.   see whether the action *might* have failed.  Some commands run in the
  750. ***************
  751. *** 923,928 ****
  752. --- 961,975 ----
  753.   \fBscroll-clear-page\fP        (boolean, default true)
  754.   Determines whether \fInn\fP clears the screen before showing each new
  755.   page of an article.
  756. + .TP
  757. + \fBscroll-last-lines\fP \fIN\fP        (integer, default 0)
  758. + Normally, \fInn\fP will show each new page of an article from the top
  759. + of the screen (with proper marking of the overlap).  When this
  760. + variable is set to a negative value, \fInn\fP will scroll the text of
  761. + the new pages from the bottom of the screen instead.  If it is set to a
  762. + positive value, \fInn\fP will show pages from the top as usual, but
  763. + switch to scrolling when there are \fIless than\fP the specified
  764. + number of lines left in the article.
  765.   .TP
  766.   \fBselect-leave-next\fP        (boolean, default false)
  767.   When set, you will be asked whether to select articles with the
  768. *** ./LAST/man/nn.1.D    Mon Jul 16 17:38:48 1990
  769. --- man/nn.1.D    Thu Jul 19 17:52:37 1990
  770. ***************
  771. *** 113,118 ****
  772. --- 113,127 ----
  773.   .B toggle
  774.   command.
  775.   .LP
  776. + Besides the options described below, you can set \fIany\fP of
  777. + \fInn\fP's variables directly on the command line via an argument of
  778. + the following format:
  779. + .sp 0.5v
  780. +     variable=value
  781. + .sp 0.5v
  782. + To set or unset a boolean variable, the value can be specified as
  783. + \fIon\fP or \fIoff\fP (\fIt\fP and \fIf\fP will also work).
  784. + .LP
  785.   Notice that the init files are read \fIbefore\fP the options are
  786.   parsed (unless you use the \-\fBI\fP option).  Therefore, the options
  787.   which are related to boolean variables set in the init file will
  788. *** ./LAST/man/nnacct.1m    Mon Jul 16 17:38:49 1990
  789. --- man/nnacct.1m    Wed Jul 18 17:06:19 1990
  790. ***************
  791. *** 5,11 ****
  792.   .SH SYNOPSIS
  793.   \fBnnacct\fP \-\fBr\fP [ \-\fBf\fP file ] [ \-\fBa\fP ] [ user ]...
  794.   .br
  795. ! \fBnnacct\fP \-\fBp\fP\fIpolicy\fP \-\fBq\fP\fIquota\fP user...
  796.   .br
  797.   \fBnnacct\fP \-\fBZERO\fP
  798.   .SH DESCRIPTION
  799. --- 5,11 ----
  800.   .SH SYNOPSIS
  801.   \fBnnacct\fP \-\fBr\fP [ \-\fBf\fP file ] [ \-\fBa\fP ] [ user ]...
  802.   .br
  803. ! \fBnnacct\fP [ \-\fBp\fP\fIpolicy\fP ] [ \-\fBq\fP\fIquota\fP ] user...
  804.   .br
  805.   \fBnnacct\fP \-\fBZERO\fP
  806.   .SH DESCRIPTION
  807. ***************
  808. *** 84,89 ****
  809. --- 84,95 ----
  810.   access policies can also be defined in this file.  This allows you to
  811.   change the policies or prices without having to recompile the whole
  812.   package since only \fInnacct\fP is modified.
  813. + .SH PRIVILEGED USERS
  814. + Normally, only root is allowed to change user policy or quota, list
  815. + all user's accounting data etc.  This privilege can be shared with
  816. + other users by listing their login name in the file $CLIENT/admins.
  817. + There should be exactly one login name per line, and no blanks are
  818. + allowed.
  819.   .SH FILES
  820.   .DT
  821.   .ta \w'$db/acctlog'u+3m
  822. ***************
  823. *** 91,96 ****
  824. --- 97,104 ----
  825.   $db/acct    accounting data (accumulated per user)
  826.   .br
  827.   $db/acctlog    accounting log (grows indefinitely)
  828. + .br
  829. + $lib/admins    login names of privileged \fInnacct\fP users.
  830.   .DT
  831.   .SH SEE ALSO
  832.   nn(1), nnusage(1)
  833. *** ./LAST/master.c    Mon Jul  9 17:59:59 1990
  834. --- master.c    Wed Jul 18 11:49:12 1990
  835. ***************
  836. *** 586,595 ****
  837.       if (kill_running) {
  838.       if (proto_lock(I_AM_MASTER, PL_TERMINATE) < 0)
  839.           temp = 0;
  840. !     else
  841. !         for (temp = 10; --temp >= 0; sleep(3))
  842. !         if (proto_lock(I_AM_MASTER, PL_CHECK) < 0) break;
  843.   
  844.       if (temp < 0) {
  845.           printf("The running master will not die....!\n");
  846.           log_entry('E', "Could not kill running master");
  847. --- 586,603 ----
  848.       if (kill_running) {
  849.       if (proto_lock(I_AM_MASTER, PL_TERMINATE) < 0)
  850.           temp = 0;
  851. !     else {
  852. !         int mpid;
  853.   
  854. +         for (temp = 10; --temp >= 0; sleep(3)) {
  855. +         sleep(3);
  856. +         mpid = proto_lock(I_AM_MASTER, PL_CHECK);
  857. +         if (mpid < 0) break;
  858. +         sleep(1);
  859. +         kill(mpid, SIGTERM); /* SIGHUP lost??? */
  860. +         }
  861. +     }
  862.       if (temp < 0) {
  863.           printf("The running master will not die....!\n");
  864.           log_entry('E', "Could not kill running master");
  865. ***************
  866. *** 600,605 ****
  867. --- 608,618 ----
  868.           !reread_groups_file && lock_message == NULL &&
  869.           group_selection == 0 && initialize < 0)
  870.           exit(0);
  871. +     }
  872. +     if (!file_exist(db_directory, "drwx")) {
  873. +     fprintf(stderr, "%s invoked with wrong user privileges\n", argv[0]);
  874. +     exit(9);
  875.       }
  876.   
  877.       if (proto_lock(I_AM_MASTER, PL_SET) != 0) {
  878. *** ./LAST/menu.c    Mon Jul 16 17:38:50 1990
  879. --- menu.c    Tue Jul 17 23:06:53 1990
  880. ***************
  881. *** 326,331 ****
  882. --- 326,332 ----
  883.   }
  884.   
  885.   export long n_selected;
  886. + export int show_art_next_invalid;
  887.   
  888.   static count_selected_articles()
  889.   {
  890. ***************
  891. *** 357,362 ****
  892. --- 358,364 ----
  893.           for (next = cur+1; next < n_articles; next++) {
  894.           if (articles[next]->attr & A_SELECT) break;
  895.           }
  896. +         show_art_next_invalid = 0;
  897.   
  898.        show:
  899.           ah = articles[cur];
  900. ***************
  901. *** 390,401 ****
  902.           break;
  903.   
  904.            case MC_DO_SELECT:
  905. -         for (temp = cur+1; temp < n_articles; temp++) {
  906. -             if (auto_select_article(ah = articles[temp], 2)) {
  907. -             ah->attr = A_SELECT;
  908. -             if (temp < next) next = temp;
  909. -             }
  910. -         }
  911.           break;
  912.   
  913.            case MC_PREV:
  914. --- 392,397 ----
  915. ***************
  916. *** 465,470 ****
  917. --- 461,470 ----
  918.           return MC_NEXTGROUP;
  919.           }
  920.   
  921. +         if (show_art_next_invalid)
  922. +         for (next = cur+1; next < n_articles; next++) {
  923. +             if (articles[next]->attr & A_SELECT) break;
  924. +         }
  925.           prev = cur; cur = next;
  926.       }
  927.   
  928. *** ./LAST/more.c    Mon Jul 16 17:38:50 1990
  929. --- more.c    Wed Jul 18 15:50:37 1990
  930. ***************
  931. *** 5,10 ****
  932. --- 5,11 ----
  933.    */
  934.   
  935.   #include "config.h"
  936. + #include "articles.h"
  937.   #include "news.h"
  938.   #include "term.h"
  939.   #include "menu.h"
  940. ***************
  941. *** 26,31 ****
  942. --- 27,33 ----
  943.   export char *trusted_escapes = NULL;
  944.   export int  new_read_prompt = 1;
  945.   export int  re_layout_more = -1;
  946. + export int  scroll_last_lines = 0;
  947.   
  948.   import int  preview_window;
  949.   import int  novice;
  950. ***************
  951. *** 93,98 ****
  952. --- 95,101 ----
  953.       'x', "Back-Ref",    &news.ng_bref,        0,
  954.       'v', "Save-File",    NULL,            0,
  955.       'a', "Spool-File",    NULL,            0,
  956. +     'i', "DB-Info",    NULL,            0,
  957.       0
  958.   };
  959.   
  960. ***************
  961. *** 147,152 ****
  962. --- 150,162 ----
  963.       if (lp == NULL) return 0;
  964.       if (expand_file_name(special, lp, 2)) lp = special;
  965.       break;
  966. +      case 'i':
  967. +     sprintf(special, "#%ld fl=%lx re=%d li=%d hp=%ld fp=%ld lp=%ld",
  968. +         (long)ah->a_number, (long)ah->flag, ah->replies, ah->lines,
  969. +         (long)ah->hpos, (long)ah->fpos, (long)ah->lpos);
  970. +     lp = special;
  971. +     break;
  972.       }
  973.       if (lp == NULL) return 0;
  974.   
  975. ***************
  976. *** 207,212 ****
  977. --- 217,244 ----
  978.       return 0;
  979.   }
  980.   
  981. + static do_auto_select()
  982. + {
  983. +     register article_header *ah;
  984. +     register int32 n;
  985. +     import int show_art_next_invalid;
  986. +     int count = 0;
  987. +     for (n = 0; n < n_articles; n++) {
  988. +     ah = articles[n];
  989. +     if (ah->attr == A_READ) continue;
  990. +     if (ah->attr & A_SELECT) continue;
  991. +     if (auto_select_article(ah, 2)) {
  992. +         ah->attr = A_AUTO_SELECT;
  993. +         show_art_next_invalid = 1;
  994. +         count++;
  995. +     }
  996. +     }
  997. +     if (count) msg("Selected %d articles", count);
  998. + }
  999.   static char *a_st_flags(flag)
  1000.   flag_type flag;
  1001.   {
  1002. ***************
  1003. *** 269,274 ****
  1004. --- 301,308 ----
  1005.       struct news_header news_save;
  1006.       struct digest_header digest_save;
  1007.       int linenum, maxline, topline, print_lines, lno1;
  1008. +     int scroll_lines, scroll_from;
  1009. +     off_t scroll_offset;
  1010.       int underline_line, fake_underline;
  1011.       int match_lines, match_redraw, match_topline, match_botline;
  1012.       int goto_line, prev_goto, stop_line, extra_lines;
  1013. ***************
  1014. *** 372,377 ****
  1015. --- 406,422 ----
  1016.       fake_underline = 0;
  1017.       shade_overlap = 0;
  1018.   
  1019. +     scroll_lines = scroll_from = 0;
  1020. +     scroll_offset = 0;
  1021. +     if (scroll_last_lines < 0)
  1022. +     scroll_from = -1;
  1023. +     else if (scroll_last_lines > 0) {
  1024. +     if (ah->lines > 0)
  1025. +         scroll_from = ah->lines - scroll_last_lines;
  1026. +     else
  1027. +         scroll_offset = ah->lpos - (scroll_last_lines * 64);
  1028. +     }
  1029.       stop_line = first_page_lines ? first_page_lines : -1;
  1030.   
  1031.       if (new_read_prompt) {
  1032. ***************
  1033. *** 877,882 ****
  1034. --- 922,938 ----
  1035.   
  1036.       if (eof && lno == screen_offset) more_return(MC_NEXT);
  1037.   
  1038. +     if (scroll_lines > 0) {
  1039. +     if (eof)
  1040. +         scroll_lines = 0;
  1041. +     else {
  1042. +         print_lines = 1;
  1043. +         scroll_lines--;
  1044. +         prompt_line = lno;
  1045. +         goto scroll_next;
  1046. +     }
  1047. +     }
  1048. +     
  1049.       raw();
  1050.   
  1051.       prompt_line = lno;
  1052. ***************
  1053. *** 950,955 ****
  1054. --- 1006,1017 ----
  1055.        case K_CONTINUE:
  1056.       if (eof) break;
  1057.       if (screen_offset == 0 && form_feed == 0 && stop_line) {
  1058. +         if ((scroll_from && linenum > scroll_from) ||
  1059. +         (scroll_offset && ftell(art) > scroll_offset)) {
  1060. +         scroll_lines = Lines - 2 - overlap;
  1061. +         print_lines = 1;
  1062. +         goto scroll;
  1063. +         }
  1064.           if (linenum > overlap) {
  1065.           underline_line = linenum;
  1066.           linenum -= overlap;
  1067. ***************
  1068. *** 1077,1083 ****
  1069.        case K_KILL_HANDLING:
  1070.       switch (kill_menu(ah)) {
  1071.        case 0:
  1072. !         more_return(MC_DO_SELECT);
  1073.        case 1:
  1074.           more_return(MC_DO_KILL);
  1075.        default:
  1076. --- 1139,1147 ----
  1077.        case K_KILL_HANDLING:
  1078.       switch (kill_menu(ah)) {
  1079.        case 0:
  1080. !         do_auto_select();
  1081. !         ah->attr = 0;
  1082. !         break;
  1083.        case 1:
  1084.           more_return(MC_DO_KILL);
  1085.        default:
  1086. ***************
  1087. *** 1147,1152 ****
  1088. --- 1211,1218 ----
  1089.           goto next_page;
  1090.   
  1091.       stop_line = -1;
  1092. +      scroll_next:
  1093.       gotoxy(0, Lines-1);
  1094.       c = print_lines + lno - Lines + 2;
  1095.       while (--c >= 0) {
  1096. *** ./LAST/news.c    Mon Jul 16 17:38:51 1990
  1097. --- news.c    Tue Jul 17 20:26:15 1990
  1098. ***************
  1099. *** 7,13 ****
  1100.   #include "config.h"
  1101.   #include "news.h"
  1102.   
  1103. ! export retry_on_error = 0;
  1104.   
  1105.   char *parse_header(f, hdr_field, modes, hdrbuf)
  1106.   FILE             *f;
  1107. --- 7,13 ----
  1108.   #include "config.h"
  1109.   #include "news.h"
  1110.   
  1111. ! export int retry_on_error = 0;
  1112.   
  1113.   char *parse_header(f, hdr_field, modes, hdrbuf)
  1114.   FILE             *f;
  1115. *** ./LAST/newsrc.c    Mon Jul 16 17:38:51 1990
  1116. --- newsrc.c    Tue Jul 17 19:17:50 1990
  1117. ***************
  1118. *** 491,541 ****
  1119.    * prepare to use newsrc & select information for a specific group
  1120.    */
  1121.   
  1122. ! static char *rc_p;        /* pointer into newsrc_line */
  1123. ! static article_number rc_min;    /* current newsrc range min */
  1124. ! static article_number rc_max;    /* current newsrc range max */
  1125. ! static char rc_delim;        /* delimiter character */
  1126. ! static char *sel_p;        /* pointer into select_line */
  1127. ! static char *sel_initp;        /* rc_p after initialization */
  1128. ! static article_number sel_min;    /* current select range min */
  1129. ! static article_number sel_max;    /* current select range max */
  1130. ! static article_number sel_digest; /* current digest */
  1131. ! static attr_type sel_type;    /* current select range type */
  1132. ! static char sel_delim;        /* delimiter character */
  1133.   
  1134.   
  1135. ! use_newsrc(gh, use_orig)
  1136. ! register group_header *gh;
  1137. ! int use_orig;
  1138.   {
  1139. ! /*    TR( ("===%s===", gh->group_name) );*/
  1140. !     if (use_orig) {
  1141. !     rc_p = gh->newsrc_orig;
  1142. !     sel_p = gh->select_orig;
  1143.       } else {
  1144. !     rc_p = gh->newsrc_line;
  1145. !     sel_p = gh->select_line;
  1146.       }
  1147.   
  1148. !     if (rc_p == NULL) {
  1149. !     rc_min = rc_max = END_OF_LIST;
  1150.       } else {
  1151. !     rc_min = rc_max = -1;
  1152. !     rc_delim = SP;
  1153. !     rc_p += gh->group_name_length + 1;
  1154.       }
  1155.   
  1156. !     sel_digest = 0;
  1157. !     if (sel_p == NULL) {
  1158. !     sel_min = sel_max = END_OF_LIST;
  1159. !     } else {
  1160. !     sel_p += gh->group_name_length + 1;
  1161. !     sel_min = sel_max = -1;
  1162. !     sel_delim = SP;
  1163.       }
  1164.   }
  1165.   /*
  1166.   #define TRC(wh)  TR( ("r%d>%-8.8s< %ld %ld %ld %c\n", wh, p ? p : "***", n, rc_min, rc_max, rc_delim) )
  1167.   #define TSEL(wh) TR( ("s%d>%-8.8s< %ld %ld %ld %c\n", wh, p ? p : "***", n, sel_min, sel_max, sel_delim) )
  1168. --- 491,560 ----
  1169.    * prepare to use newsrc & select information for a specific group
  1170.    */
  1171.   
  1172. ! static struct rc_info {
  1173. !     char *rc_p;            /* pointer into newsrc_line */
  1174. !     article_number rc_min;    /* current newsrc range min */
  1175. !     article_number rc_max;    /* current newsrc range max */
  1176. !     char rc_delim;        /* delimiter character */
  1177. !     char *sel_p;        /* pointer into select_line */
  1178. !     char *sel_initp;        /* rc_p after initialization */
  1179. !     article_number sel_min;    /* current select range min */
  1180. !     article_number sel_max;    /* current select range max */
  1181. !     article_number sel_digest;    /* current digest */
  1182. !     attr_type sel_type;        /* current select range type */
  1183. !     char sel_delim;        /* delimiter character */
  1184. ! } orig, cur;
  1185.   
  1186. + static int rctest_mode;
  1187.   
  1188. ! static init_rctest(gh, r)
  1189. ! group_header *gh;
  1190. ! register struct rc_info *r;
  1191.   {
  1192. !     if (r->rc_p == NULL) {
  1193. !     r->rc_min = r->rc_max = END_OF_LIST;
  1194.       } else {
  1195. !     r->rc_min = r->rc_max = -1;
  1196. !     r->rc_delim = SP;
  1197. !     r->rc_p += gh->group_name_length + 1;
  1198.       }
  1199.   
  1200. !     r->sel_digest = 0;
  1201. !     if (r->sel_p == NULL) {
  1202. !     r->sel_min = r->sel_max = END_OF_LIST;
  1203.       } else {
  1204. !     r->sel_p += gh->group_name_length + 1;
  1205. !     r->sel_min = r->sel_max = -1;
  1206. !     r->sel_delim = SP;
  1207.       }
  1208. + }
  1209. + use_newsrc(gh, mode)
  1210. + register group_header *gh;
  1211. + int mode;
  1212. + {
  1213. +     orig.rc_p = gh->newsrc_orig;
  1214. +     orig.sel_p = gh->select_orig;
  1215. +     cur.rc_p = gh->newsrc_line;
  1216. +     cur.sel_p = gh->select_line;
  1217.   
  1218. !     rctest_mode = mode;
  1219. !     switch (mode) {
  1220. !      case 0:
  1221. !     init_rctest(gh, &cur);
  1222. !     break;
  1223. !      case 1:
  1224. !     init_rctest(gh, &orig);
  1225. !     break;
  1226. !      case 2:
  1227. !     init_rctest(gh, &cur);
  1228. !     init_rctest(gh, &orig);
  1229. !     break;
  1230.       }
  1231.   }
  1232.   /*
  1233.   #define TRC(wh)  TR( ("r%d>%-8.8s< %ld %ld %ld %c\n", wh, p ? p : "***", n, rc_min, rc_max, rc_delim) )
  1234.   #define TSEL(wh) TR( ("s%d>%-8.8s< %ld %ld %ld %c\n", wh, p ? p : "***", n, sel_min, sel_max, sel_delim) )
  1235. ***************
  1236. *** 543,570 ****
  1237.   #define TRC(wh)
  1238.   #define TSEL(wh)
  1239.   
  1240. ! attr_type test_article(ah)
  1241.   register article_header *ah;
  1242.   {
  1243.       register char *p;
  1244.       register int c;
  1245.       register int32 n = ah->a_number, x;
  1246.   
  1247. !     while (n > rc_max) {
  1248.       /* get next interval from newsrc line */
  1249. !     rc_min = -1;
  1250.       x = 0;
  1251. !     p = rc_p;
  1252.       TRC(1);
  1253.   
  1254.       if (*p == RC_DELIM) p++;
  1255.       if (*p == NUL || *p == NL)
  1256. !         rc_min = rc_max = END_OF_LIST;
  1257.       else {
  1258.           for ( ; (c = *p) && c != RC_DELIM && c != NL; p++) {
  1259.           if (c == RC_RANGE) {
  1260. !             if (rc_min < 0)
  1261. !             rc_min = x;
  1262.               else
  1263.               msg("syntax error in rc file");
  1264.               x = 0;
  1265. --- 562,590 ----
  1266.   #define TRC(wh)
  1267.   #define TSEL(wh)
  1268.   
  1269. ! static attr_type rctest(ah, r)
  1270.   register article_header *ah;
  1271. + register struct rc_info *r;
  1272.   {
  1273.       register char *p;
  1274.       register int c;
  1275.       register int32 n = ah->a_number, x;
  1276.   
  1277. !     while (n > r->rc_max) {
  1278.       /* get next interval from newsrc line */
  1279. !     r->rc_min = -1;
  1280.       x = 0;
  1281. !     p = r->rc_p;
  1282.       TRC(1);
  1283.   
  1284.       if (*p == RC_DELIM) p++;
  1285.       if (*p == NUL || *p == NL)
  1286. !         r->rc_min = r->rc_max = END_OF_LIST;
  1287.       else {
  1288.           for ( ; (c = *p) && c != RC_DELIM && c != NL; p++) {
  1289.           if (c == RC_RANGE) {
  1290. !             if (r->rc_min < 0)
  1291. !             r->rc_min = x;
  1292.               else
  1293.               msg("syntax error in rc file");
  1294.               x = 0;
  1295. ***************
  1296. *** 574,604 ****
  1297.           if (isascii(*p) && isdigit(*p))
  1298.               x = x*10 + c - '0';
  1299.           }
  1300. !         rc_max = x;
  1301. !         if (rc_min < 0) rc_min = x;
  1302. !         rc_p = p;
  1303.       }
  1304.       }
  1305.       TRC(2);
  1306.   
  1307. !     if (n >= rc_min && n <= rc_max) return A_READ;
  1308.   
  1309. !     p = sel_p;
  1310. !     if (sel_digest != 0) {
  1311. !     if (n == sel_digest && (ah->flag & A_DIGEST)) {
  1312. !         if (*sel_p == SEL_END_DIGEST) return A_READ;
  1313.           n = ah->fpos;
  1314.       } else {
  1315. !         if (n < sel_digest) return 0;
  1316.           while (*p && *p++ != SEL_END_DIGEST);
  1317. !         sel_digest = 0;
  1318. !         sel_min = sel_max = -1;
  1319.       }
  1320.       }
  1321.   
  1322. !     while (n > sel_max) {
  1323. !     sel_min = -1;
  1324. !     sel_type = A_SELECT;
  1325.       x = 0;
  1326.       TSEL(3);
  1327.   
  1328. --- 594,624 ----
  1329.           if (isascii(*p) && isdigit(*p))
  1330.               x = x*10 + c - '0';
  1331.           }
  1332. !         r->rc_max = x;
  1333. !         if (r->rc_min < 0) r->rc_min = x;
  1334. !         r->rc_p = p;
  1335.       }
  1336.       }
  1337.       TRC(2);
  1338.   
  1339. !     if (n >= r->rc_min && n <= r->rc_max) return A_READ;
  1340.   
  1341. !     p = r->sel_p;
  1342. !     if (r->sel_digest != 0) {
  1343. !     if (n == r->sel_digest && (ah->flag & A_DIGEST)) {
  1344. !         if (*(r->sel_p) == SEL_END_DIGEST) return A_READ;
  1345.           n = ah->fpos;
  1346.       } else {
  1347. !         if (n < r->sel_digest) return 0;
  1348.           while (*p && *p++ != SEL_END_DIGEST);
  1349. !         r->sel_digest = 0;
  1350. !         r->sel_min = r->sel_max = -1;
  1351.       }
  1352.       }
  1353.   
  1354. !     while (n > r->sel_max) {
  1355. !     r->sel_min = -1;
  1356. !     r->sel_type = A_SELECT;
  1357.       x = 0;
  1358.       TSEL(3);
  1359.   
  1360. ***************
  1361. *** 605,623 ****
  1362.       for (;;) {
  1363.           switch (*p) {
  1364.            case SEL_SELECT:
  1365. !         sel_type = A_SELECT;
  1366.           p++;
  1367.           continue;
  1368.            case SEL_LEAVE:
  1369. !         sel_type = A_LEAVE;
  1370.           p++;
  1371.           continue;
  1372.            case SEL_SEEN:
  1373. !         sel_type = A_SEEN;
  1374.           p++;
  1375.           continue;
  1376.            case SEL_UNREAD:
  1377. !         sel_type = 0;
  1378.           p++;
  1379.           continue;
  1380.            case SEL_DIGEST:
  1381. --- 625,643 ----
  1382.       for (;;) {
  1383.           switch (*p) {
  1384.            case SEL_SELECT:
  1385. !         r->sel_type = A_SELECT;
  1386.           p++;
  1387.           continue;
  1388.            case SEL_LEAVE:
  1389. !         r->sel_type = A_LEAVE;
  1390.           p++;
  1391.           continue;
  1392.            case SEL_SEEN:
  1393. !         r->sel_type = A_SEEN;
  1394.           p++;
  1395.           continue;
  1396.            case SEL_UNREAD:
  1397. !         r->sel_type = 0;
  1398.           p++;
  1399.           continue;
  1400.            case SEL_DIGEST:
  1401. ***************
  1402. *** 624,638 ****
  1403.           while (*p && *p++ != SEL_END_DIGEST);
  1404.           continue;
  1405.            case SEL_END_DIGEST:
  1406. !         if (sel_digest) {
  1407. !             if (sel_digest == ah->a_number) {
  1408. !             sel_p = p;
  1409.               return A_READ;
  1410.               }
  1411. !             sel_digest = 0;
  1412.           }
  1413.           p++;
  1414. !         sel_type = A_SELECT;
  1415.           continue;
  1416.            default:
  1417.           break;
  1418. --- 644,658 ----
  1419.           while (*p && *p++ != SEL_END_DIGEST);
  1420.           continue;
  1421.            case SEL_END_DIGEST:
  1422. !         if (r->sel_digest) {
  1423. !             if (r->sel_digest == ah->a_number) {
  1424. !             r->sel_p = p;
  1425.               return A_READ;
  1426.               }
  1427. !             r->sel_digest = 0;
  1428.           }
  1429.           p++;
  1430. !         r->sel_type = A_SELECT;
  1431.           continue;
  1432.            default:
  1433.           break;
  1434. ***************
  1435. *** 641,647 ****
  1436.       }
  1437.   
  1438.       if (*p == NUL || *p == NL) {
  1439. !         sel_min = sel_max = END_OF_LIST;
  1440.           break;
  1441.       }
  1442.   
  1443. --- 661,667 ----
  1444.       }
  1445.   
  1446.       if (*p == NUL || *p == NL) {
  1447. !         r->sel_min = r->sel_max = END_OF_LIST;
  1448.           break;
  1449.       }
  1450.   
  1451. ***************
  1452. *** 667,674 ****
  1453.           break;
  1454.   
  1455.            case SEL_RANGE:
  1456. !         if (sel_min < 0)
  1457. !             sel_min = x;
  1458.           else
  1459.               msg("syntax error in sel file");
  1460.           x = 0;
  1461. --- 687,694 ----
  1462.           break;
  1463.   
  1464.            case SEL_RANGE:
  1465. !         if (r->sel_min < 0)
  1466. !             r->sel_min = x;
  1467.           else
  1468.               msg("syntax error in sel file");
  1469.           x = 0;
  1470. ***************
  1471. *** 682,690 ****
  1472.               break;
  1473.           }
  1474.           p++;
  1475. !         sel_digest = x;
  1476. !         if (n < sel_digest) {
  1477. !             sel_p = p;
  1478.               return 0;
  1479.           }
  1480.           n = ah->fpos;
  1481. --- 702,710 ----
  1482.               break;
  1483.           }
  1484.           p++;
  1485. !         r->sel_digest = x;
  1486. !         if (n < r->sel_digest) {
  1487. !             r->sel_p = p;
  1488.               return 0;
  1489.           }
  1490.           n = ah->fpos;
  1491. ***************
  1492. *** 692,725 ****
  1493.           break;
  1494.   
  1495.            case NL:
  1496. !         if (sel_digest == 0) break;
  1497.           /* fall thru */
  1498.            case SEL_END_DIGEST:
  1499. !         if (sel_digest == ah->a_number) {
  1500. !             sel_p = p;
  1501. !             return (ah->fpos == x) ? sel_type : A_READ;
  1502.           }
  1503. !         sel_digest = 0;
  1504.           x = -1;
  1505.           break;
  1506.           }
  1507.           break;
  1508.       }
  1509. !     sel_max = x;
  1510. !     if (sel_min < 0) sel_min = x;
  1511. !     sel_p = p;
  1512.       }
  1513.   
  1514. !     if (n >= sel_min && n <= sel_max) return sel_type;
  1515.   
  1516. !     if (sel_digest) return A_READ; /* only read articles are not listed */
  1517.   
  1518.       return 0;    /* unread, unseen, unselected */
  1519.   }
  1520.   
  1521.   /*
  1522.    * We only mark the articles that should remain unread
  1523.    */
  1524.   
  1525.   /*VARARGS*/
  1526.   static append(va_alist)
  1527. --- 712,773 ----
  1528.           break;
  1529.   
  1530.            case NL:
  1531. !         if (r->sel_digest == 0) break;
  1532.           /* fall thru */
  1533.            case SEL_END_DIGEST:
  1534. !         if (r->sel_digest == ah->a_number) {
  1535. !             r->sel_p = p;
  1536. !             return (ah->fpos == x) ? r->sel_type : A_READ;
  1537.           }
  1538. !         r->sel_digest = 0;
  1539.           x = -1;
  1540.           break;
  1541.           }
  1542.           break;
  1543.       }
  1544. !     r->sel_max = x;
  1545. !     if (r->sel_min < 0) r->sel_min = x;
  1546. !     r->sel_p = p;
  1547.       }
  1548.   
  1549. !     if (n >= r->sel_min && n <= r->sel_max) return r->sel_type;
  1550.   
  1551. !     if (r->sel_digest) return A_READ; /* only read articles are not listed */
  1552.   
  1553.       return 0;    /* unread, unseen, unselected */
  1554.   }
  1555.   
  1556. + attr_type test_article(ah)
  1557. + article_header *ah;
  1558. + {
  1559. +     attr_type a;
  1560. +     switch (rctest_mode) {
  1561. +      case 0:
  1562. +     return rctest(ah, &cur);
  1563. +      case 1:
  1564. +     return rctest(ah, &orig);
  1565. +      case 2:
  1566. +     a = rctest(ah, &cur);
  1567. +     if (a != A_READ) return a;
  1568. +     if (rctest(ah, &orig) == A_READ) return A_KILL;
  1569. +     return a;
  1570. +     }
  1571. + }
  1572.   /*
  1573.    * We only mark the articles that should remain unread
  1574.    */
  1575. + static char *rc_p;        /* pointer into newsrc_line */
  1576. + static article_number rc_min;    /* current newsrc range min */
  1577. + static char rc_delim;        /* delimiter character */
  1578. + static char *sel_p;        /* pointer into select_line */
  1579. + static char *sel_initp;        /* rc_p after initialization */
  1580. + static article_number sel_min;    /* current select range min */
  1581. + static article_number sel_max;    /* current select range max */
  1582. + static article_number sel_digest; /* current digest */
  1583. + static char sel_delim;        /* delimiter character */
  1584.   
  1585.   /*VARARGS*/
  1586.   static append(va_alist)
  1587. ***************
  1588. *** 821,826 ****
  1589. --- 869,876 ----
  1590.    out:
  1591.       if ((gh->last_article = get_last_article(gh)) < 0)
  1592.       gh->last_article = 0;
  1593. +     if (gh->last_article < gh->first_article)
  1594. +     gh->first_article = gh->last_article;
  1595.   
  1596.       gh->group_flag |= G_READ;    /* should not call update_group again */
  1597.       if (mark_counter > 0) {
  1598. ***************
  1599. *** 1072,1084 ****
  1600.       count = gh->unread_count;
  1601.   
  1602.       for (n = gh->last_article + 1; n <= last; n++) {
  1603. !     if (rc_min == END_OF_LIST) {
  1604.           /* current & rest is unread */
  1605.           last = n - 1;
  1606.           break;
  1607.       }
  1608.       ahdr.a_number = n;
  1609. !     if ((attr = test_article(&ahdr)) == A_READ) continue;
  1610.       if (at >= atmax) {
  1611.           atmax += 100;
  1612.           numtab = resizeobj(numtab, article_number, atmax);
  1613. --- 1122,1134 ----
  1614.       count = gh->unread_count;
  1615.   
  1616.       for (n = gh->last_article + 1; n <= last; n++) {
  1617. !     if (cur.rc_min == END_OF_LIST) {
  1618.           /* current & rest is unread */
  1619.           last = n - 1;
  1620.           break;
  1621.       }
  1622.       ahdr.a_number = n;
  1623. !     if ((attr = rctest(&ahdr, &cur)) == A_READ) continue;
  1624.       if (at >= atmax) {
  1625.           atmax += 100;
  1626.           numtab = resizeobj(numtab, article_number, atmax);
  1627. ***************
  1628. *** 1213,1223 ****
  1629.       for (ahdr.a_number = gh->last_article + 1;
  1630.            ahdr.a_number <= gh->last_db_article;
  1631.            ahdr.a_number++) {
  1632. !         if (rc_min == END_OF_LIST) {
  1633.           gh->unread_count += gh->last_db_article - ahdr.a_number + 1;
  1634.           break;
  1635.           }
  1636. !         if (test_article(&ahdr) != A_READ)
  1637.           gh->unread_count++;
  1638.       }
  1639.       }
  1640. --- 1263,1273 ----
  1641.       for (ahdr.a_number = gh->last_article + 1;
  1642.            ahdr.a_number <= gh->last_db_article;
  1643.            ahdr.a_number++) {
  1644. !         if (cur.rc_min == END_OF_LIST) {
  1645.           gh->unread_count += gh->last_db_article - ahdr.a_number + 1;
  1646.           break;
  1647.           }
  1648. !         if (rctest(&ahdr, &cur) != A_READ)
  1649.           gh->unread_count++;
  1650.       }
  1651.       }
  1652. *** ./LAST/nn.c    Mon Jul 16 17:38:52 1990
  1653. --- nn.c    Tue Jul 17 18:25:26 1990
  1654. ***************
  1655. *** 37,42 ****
  1656. --- 37,43 ----
  1657.       do_kill_handling = 1,
  1658.       group_name_args = 0,
  1659.       merged_menu = 0,
  1660. +     prev_also_read = 1,
  1661.       repeat_group_query = 0,
  1662.       report_cost_on_exit = 1,
  1663.       show_motd_on_entry = 1,
  1664. ***************
  1665. *** 251,256 ****
  1666. --- 252,259 ----
  1667.           prev = tmp;
  1668.           if (gh->unread_count <= 0)
  1669.           group_mode |= ACC_ORIG_NEWSRC;
  1670. +         else if (prev_also_read)
  1671. +         group_mode |= ACC_MERGED_NEWSRC;
  1672.           goto enter_direct;
  1673.   
  1674.        case ME_NEXT:
  1675. *** ./LAST/nntp.c    Mon Jul  9 18:00:04 1990
  1676. --- nntp.c    Tue Jul 17 20:49:19 1990
  1677. ***************
  1678. *** 51,57 ****
  1679.   export int nntp_failed = 0;    /* bool: t iff connection is broken in
  1680.                      nntp_get_article() or nntp_get_active() */
  1681.   
  1682. ! export nntp_cache_size = NNTPCACHE;
  1683.   export char *nntp_cache_dir = NULL;
  1684.   
  1685.   export int nntp_local_server = 0;
  1686. --- 51,57 ----
  1687.   export int nntp_failed = 0;    /* bool: t iff connection is broken in
  1688.                      nntp_get_article() or nntp_get_active() */
  1689.   
  1690. ! export int nntp_cache_size = NNTPCACHE;
  1691.   export char *nntp_cache_dir = NULL;
  1692.   
  1693.   export int nntp_local_server = 0;
  1694. *** ./LAST/patchlevel.h    Mon Jul 16 17:38:53 1990
  1695. --- patchlevel.h    Wed Jul 18 16:05:11 1990
  1696. ***************
  1697. *** 19,25 ****
  1698.    *    1990-06-25: Patch #6 (6.4.6) - MEDIUM
  1699.    *    1990-07-09: Patch #7 (6.4.7) - LOW
  1700.    *    1990-07-16: Patch #8 (6.4.8) - HIGH
  1701.    */
  1702.   
  1703. ! #define PATCHLEVEL 8
  1704.   
  1705. --- 19,26 ----
  1706.    *    1990-06-25: Patch #6 (6.4.6) - MEDIUM
  1707.    *    1990-07-09: Patch #7 (6.4.7) - LOW
  1708.    *    1990-07-16: Patch #8 (6.4.8) - HIGH
  1709. +  *    1990-07-19: Patch #9 (6.4.9) - MEDIUM
  1710.    */
  1711.   
  1712. ! #define PATCHLEVEL 9
  1713.   
  1714. *** ./LAST/sequence.c    Tue May 22 12:53:57 1990
  1715. --- sequence.c    Wed Jul 18 19:54:09 1990
  1716. ***************
  1717. *** 430,435 ****
  1718. --- 430,437 ----
  1719.       register group_header *gh;
  1720.       group_header *get_group_search();
  1721.       register char *group;
  1722. +     register char *value;
  1723. +     int non_vars;
  1724.       int found, any, errors, gnum;
  1725.   
  1726.       group_sequence = NULL;
  1727. ***************
  1728. *** 436,442 ****
  1729. --- 438,446 ----
  1730.       also_subgroups = 0;
  1731.   
  1732.       any = errors = 0;
  1733. +     non_vars = 0;
  1734.       while (group = *groups++) {
  1735. +     non_vars++;
  1736.   
  1737.       if (hex_group_args) {
  1738.           sscanf(group, "%x", &gnum);
  1739. ***************
  1740. *** 451,456 ****
  1741. --- 455,467 ----
  1742.           continue;
  1743.       }
  1744.   
  1745. +     if (value = strchr(group, '=')) {
  1746. +         *value++ = NUL;
  1747. +         set_variable(group, 1, value);
  1748. +         non_vars--;
  1749. +         continue;
  1750. +     }
  1751.       if (*group == '+' || *group == '~' || file_exist(group, "fr")) {
  1752.           faked_entry(group, G_FOLDER);
  1753.           any++;
  1754. ***************
  1755. *** 490,495 ****
  1756. --- 501,509 ----
  1757.       } else
  1758.           any++;
  1759.       }
  1760. +     if (non_vars == 0)
  1761. +     return normal_group_sequence();
  1762.   
  1763.       end_sequence();
  1764.   
  1765. *** ./LAST/term.c    Mon Jul 16 17:38:54 1990
  1766. --- term.c    Tue Jul 17 21:21:16 1990
  1767. ***************
  1768. *** 317,322 ****
  1769. --- 317,324 ----
  1770.   
  1771.       if (batch_mode) {
  1772.       term_name = "batch";
  1773. +     close(0);
  1774. +     open("/dev/null", 0);
  1775.       return;
  1776.       }
  1777.   
  1778. ***************
  1779. *** 1078,1090 ****
  1780.   
  1781.       if (completion != NULL_FCT) {
  1782.           if (comp_used && c == erase_key) {
  1783. -         CALL(completion)(buf, -1);
  1784. -         if (did_help) { clrmsg(i); did_help = 0; }
  1785.           if (comp_len) {
  1786.               i -= comp_len;
  1787.               while (--comp_len >= 0) putchar(BS);
  1788.               clrline();
  1789.           }
  1790.           comp_len = comp_used = 0;
  1791.           if (lastc == help_key) goto no_completion;
  1792.           continue;
  1793. --- 1080,1093 ----
  1794.   
  1795.       if (completion != NULL_FCT) {
  1796.           if (comp_used && c == erase_key) {
  1797.           if (comp_len) {
  1798.               i -= comp_len;
  1799.               while (--comp_len >= 0) putchar(BS);
  1800.               clrline();
  1801.           }
  1802. +         if (!CALL(completion)(buf, -(i+1)) && did_help)
  1803. +             clrmsg(i);
  1804. +         did_help = 0;
  1805.           comp_len = comp_used = 0;
  1806.           if (lastc == help_key) goto no_completion;
  1807.           continue;
  1808. ***************
  1809. *** 1145,1152 ****
  1810.           }
  1811.   
  1812.           if (comp_used) {
  1813. !         CALL(completion)(buf, -1);
  1814. !         if (did_help) clrmsg(i);
  1815.           comp_len = comp_used = 0;
  1816.           }
  1817.       }
  1818. --- 1148,1156 ----
  1819.           }
  1820.   
  1821.           if (comp_used) {
  1822. !         if (!CALL(completion)(buf, -(i+1)) && did_help)
  1823. !             clrmsg(i);
  1824. !         did_help = 0;
  1825.           comp_len = comp_used = 0;
  1826.           }
  1827.       }
  1828. *** ./LAST/variable.c    Mon Jul 16 17:38:55 1990
  1829. --- variable.c    Wed Jul 18 19:54:09 1990
  1830. ***************
  1831. *** 94,99 ****
  1832. --- 94,100 ----
  1833.       monitor_mode,
  1834.       new_read_prompt,
  1835.       novice,
  1836. +     prev_also_read,
  1837.       preview_mark_read,
  1838.       query_signature,
  1839.       quick_save,
  1840. ***************
  1841. *** 155,160 ****
  1842. --- 156,162 ----
  1843.       response_check_pause,
  1844.       retry_on_error,
  1845.       save_counter_offset,
  1846. +     scroll_last_lines,
  1847.       show_purpose_mode,
  1848.       slow_speed,
  1849.       sort_mode,
  1850. ***************
  1851. *** 320,325 ****
  1852. --- 322,328 ----
  1853.       "patch-command",        STR SAFE 1,    (char **)patch_command,
  1854.       "preview-continuation",     INT 0,        (char **)&preview_continuation,
  1855.       "preview-mark-read",    BOOL 0,        (char **)&preview_mark_read,
  1856. +     "previous-also-read",    BOOL 0,        (char **)&prev_also_read,
  1857.       "print-header-lines",    STR 0,        (char **)&print_header_lines,
  1858.       "print-header-type",    INT 0,        (char **)&print_header_type,
  1859.       "printer",            STR SAFE 1,    (char **)printer,
  1860. ***************
  1861. *** 342,347 ****
  1862. --- 345,351 ----
  1863.       "save-header-lines",    STR 0,        (char **)&save_header_lines,
  1864.       "save-report",        BOOL 0,        (char **)&save_report,
  1865.       "scroll-clear-page",    BOOL 0,        (char **)&scroll_clear_page,
  1866. +     "scroll-last-lines",    INT 0,        (char **)&scroll_last_lines,
  1867.       "select-leave-next",    BOOL 0,        (char **)&select_leave_next,
  1868.       "select-on-sender",        BOOL 0,        (char **)&select_on_sender,
  1869.       "shading-off",        CODES 0,    (char **)&shade_off_attr,
  1870. ***************
  1871. *** 604,609 ****
  1872. --- 608,614 ----
  1873.           article_limit = (on && value > 0) ? value : -1;
  1874.           break;
  1875.       }
  1876. +     break;
  1877.   
  1878.        case V_CODES:
  1879.       {
  1880. ***************
  1881. *** 665,670 ****
  1882. --- 670,676 ----
  1883.       register char *str;
  1884.       register int b;
  1885.   
  1886. +     if (tag != NULL)
  1887.       *tag = var_on_stack(var) ? '>' :
  1888.       (var->var_flags & V_MODIFIED) ? '*' : ' ';
  1889.   
  1890. ***************
  1891. *** 752,757 ****
  1892. --- 758,770 ----
  1893.       return res;
  1894.   }
  1895.   
  1896. + static int vc_column;
  1897. + var_compl_opts(col)
  1898. + int col;
  1899. + {
  1900. +     vc_column = col;
  1901. + }
  1902.   
  1903.   var_completion(path, index)
  1904.   char *path;
  1905. ***************
  1906. *** 760,767 ****
  1907.       static char *head, *tail = NULL;
  1908.       static int len;
  1909.       static struct variable_defs *var, *help_var;
  1910.   
  1911. !     if (index < 0) return 0;
  1912.   
  1913.       if (path) {
  1914.       head = path;
  1915. --- 773,784 ----
  1916.       static char *head, *tail = NULL;
  1917.       static int len;
  1918.       static struct variable_defs *var, *help_var;
  1919. +     extern int prompt_length, prompt_line;
  1920.   
  1921. !     if (index < 0) {
  1922. !     clrmsg(-(index + 1));
  1923. !     return 1;
  1924. !     }
  1925.   
  1926.       if (path) {
  1927.       head = path;
  1928. ***************
  1929. *** 807,815 ****
  1930. --- 824,835 ----
  1931.       if (index < 0) continue;
  1932.       if (index > 0) break;
  1933.       sprintf(tail, "%s ", var->var_name + len);
  1934. +     msg("%.70s", var_value(var, (char *)NULL));
  1935. +     gotoxy(prompt_length + vc_column, prompt_line);
  1936.       var++;
  1937.       return 1;
  1938.       }
  1939. +     clrmsg(vc_column);
  1940.       return 0;
  1941.   }
  1942.   
  1943. ***************
  1944. *** 977,984 ****
  1945.       return 0;
  1946.   }
  1947.   
  1948. ! disp_variables(all)
  1949.   int all;
  1950.   {
  1951.       char *str, pushed;
  1952.       int b;
  1953. --- 997,1005 ----
  1954.       return 0;
  1955.   }
  1956.   
  1957. ! disp_variables(all, rexp)
  1958.   int all;
  1959. + char *rexp;
  1960.   {
  1961.       char *str, pushed;
  1962.       int b;
  1963. ***************
  1964. *** 988,1002 ****
  1965.   
  1966.       if (in_init) return;
  1967.   
  1968. -     pg_init(0, 1);
  1969.       clrdisp();
  1970. !     if (novice && !all) {
  1971.       msg("Use `:set all' to see all variable settings");
  1972.       home();
  1973.       }
  1974. !     pg_next();
  1975. !     so_printf("Variable settings:");
  1976.   
  1977.       for (var = variables; var < &variables[TABLE_SIZE]; var++) {
  1978.       if (pg_regexp != NULL && regexec(pg_regexp, var->var_name) == 0)
  1979. --- 1009,1026 ----
  1980.   
  1981.       if (in_init) return;
  1982.   
  1983.       clrdisp();
  1984. !     if (novice && !all && rexp == NULL) {
  1985.       msg("Use `:set all' to see all variable settings");
  1986.       home();
  1987.       }
  1988. !     so_printxy(0, 0, "Variable settings");
  1989. !     pg_init(1, 1);
  1990. !     if (rexp) {
  1991. !     pg_regexp = regcomp(rexp + 1);
  1992. !     all = 1;
  1993. !     }
  1994.   
  1995.       for (var = variables; var < &variables[TABLE_SIZE]; var++) {
  1996.       if (pg_regexp != NULL && regexec(pg_regexp, var->var_name) == 0)
  1997.